﻿using System;
using System.Diagnostics.Contracts;
using System.Xml.Serialization;

namespace MathService.Vectors
{
    [Serializable]
    [System.ComponentModel.TypeConverter(typeof(Vector2DConverter))]
    public struct Vector2D : IEquatable<Vector2D>, ICloneable<Vector2D>
    {
        private Complex f_Copmlex;

        [XmlAttribute]
        public double X { get { return f_Copmlex.Re; } set { f_Copmlex.Re = value; } }

        [XmlAttribute]
        public double Y { get { return f_Copmlex.Im; } set { f_Copmlex.Im = value; } }

        [XmlIgnore]
        public double R { get { return f_Copmlex.Abs; } set { f_Copmlex.Abs = value; } }

        [XmlIgnore]
        public double Angle { get { return f_Copmlex.Arg; } set { f_Copmlex.Arg = value; } }

        public Vector2D(double X, double Y)
        {
            f_Copmlex = new Complex(X, Y);
        }
        private Vector2D(Complex Z) { f_Copmlex = Z; }

        public Vector2D InBasis(Basis2D b)
        {
            return new Vector2D(b.xx * f_Copmlex.Re + b.xy * f_Copmlex.Im, b.yx * f_Copmlex.Re + b.yy * f_Copmlex.Im);
        }

        public override string ToString() { return string.Format("({0};{1})", X, Y); }

        //public override int GetHashCode() { return ToString().GetHashCode(); }

        public Vector2D Clone() { return new Vector2D(f_Copmlex); }

        object ICloneable.Clone() { return Clone(); }

        public bool Equals(Vector2D other) { return f_Copmlex.Equals(other.f_Copmlex); }

        public override bool Equals(object obj) { return obj is Vector2D && Equals((Vector2D)obj); }

        public override int GetHashCode() { return f_Copmlex.GetHashCode(); }

        #region Операторы

        public static bool operator ==(Vector2D a, Vector2D b) { return a.Equals(b); }
        public static bool operator !=(Vector2D a, Vector2D b) { return !(a == b); }

        public static explicit operator Complex(Vector2D P) { return P.f_Copmlex; }
        public static explicit operator Vector2D(Complex Z) { return new Vector2D(Z); }

        public static Vector2D operator +(Vector2D a) { return new Vector2D(a.X, a.Y); }
        public static Vector2D operator -(Vector2D a) { return new Vector2D(-a.X, -a.Y); }

        public static double operator *(Vector2D a, Vector2D b) { return a.X * b.X + a.Y * b.Y; }

        public static Vector2D operator +(Vector2D a, double b) { return new Vector2D(a.X + b, a.Y + b); }
        public static Vector2D operator -(Vector2D a, double b) { return new Vector2D(a.X - b, a.Y - b); }

        public static Vector2D operator *(Vector2D a, double b) { return new Vector2D(a.X * b, a.Y * b); }
        public static Vector2D operator /(Vector2D a, double b)
        {
            Contract.Requires(Math.Abs(b) > double.Epsilon, "b == 0");
            return new Vector2D(a.X / b, a.Y / b);
        }

        public static Vector2D operator +(double a, Vector2D b) { return new Vector2D(a + b.X, a + b.Y); }
        public static Vector2D operator -(double a, Vector2D b) { return new Vector2D(a - b.X, a - b.Y); }

        public static Vector2D operator *(double a, Vector2D b) { return new Vector2D(a * b.X, a * b.Y); }
        public static Vector2D operator /(double a, Vector2D b)
        {
            Contract.Requires(b.X != 0, "b.X == 0");
            Contract.Requires(b.Y != 0, "b.Y == 0");
            return new Vector2D(a / b.X, a / b.Y);
        }

        public static double operator ^(Vector2D a, Vector2D b) { return (a * b) / (a.R * b.R); }
        public static bool operator |(Vector2D a, Vector2D b) { return (a ^ b) == 1; }
        public static bool operator &(Vector2D a, Vector2D b) { return (a ^ b) == 0; }
        public static double operator %(Vector2D a, Vector2D b)
        {
            Contract.Requires(b.R != 0, "b.R == 0");
            return (a * b) / b.R;
        }
        public static Vector2D operator %(Vector2D a, Basis2D b) { return a.InBasis(b); }

        public static Vector2D operator +(Vector2D a, float b) { return new Vector2D(a.X + b, a.Y + b); }
        public static Vector2D operator -(Vector2D a, float b) { return new Vector2D(a.X - b, a.Y - b); }

        public static Vector2D operator *(Vector2D a, float b) { return new Vector2D(a.X * b, a.Y * b); }
        public static Vector2D operator /(Vector2D a, float b)
        {
            Contract.Requires(b != 0, "b == 0");
            return new Vector2D(a.X / b, a.Y / b);
        }

        public static Vector2D operator +(float a, Vector2D b) { return new Vector2D(a + b.X, a + b.Y); }
        public static Vector2D operator -(float a, Vector2D b) { return new Vector2D(a - b.X, a - b.Y); }

        public static Vector2D operator *(float a, Vector2D b) { return new Vector2D(a * b.X, a * b.Y); }
        public static Vector2D operator /(float a, Vector2D b)
        {
            Contract.Requires(b.X != 0, "b.X == 0");
            Contract.Requires(b.Y != 0, "b.Y == 0");
            return new Vector2D(a / b.X, a / b.Y);
        }

        public static Vector2D operator +(Vector2D a, int b) { return new Vector2D(a.X + b, a.Y + b); }
        public static Vector2D operator -(Vector2D a, int b) { return new Vector2D(a.X - b, a.Y - b); }

        public static Vector2D operator *(Vector2D a, int b) { return new Vector2D(a.X * b, a.Y * b); }
        public static Vector2D operator /(Vector2D a, int b)
        {
            Contract.Requires(b != 0, "b == 0");
            return new Vector2D(a.X / b, a.Y / b);
        }

        public static Vector2D operator +(int a, Vector2D b) { return new Vector2D(a + b.X, a + b.Y); }
        public static Vector2D operator -(int a, Vector2D b) { return new Vector2D(a - b.X, a - b.Y); }

        public static Vector2D operator *(int a, Vector2D b) { return new Vector2D(a * b.X, a * b.Y); }
        public static Vector2D operator /(int a, Vector2D b)
        {
            Contract.Requires(b.X != 0, "b.X == 0");
            Contract.Requires(b.Y != 0, "b.Y == 0");
            return new Vector2D(a / b.X, a / b.Y);
        }

        #endregion


    }
}
